﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using PetaPoco;

namespace Common.Orm
{
    public class Repository<T> : IRepository<T>
    {
        private readonly Database db = DbContext.GetDbContext();
        private readonly string tableName;

        public Repository()
        {
            tableName = typeof(T).Name;
        }

        public object Add(T t)
        {
            return db.Insert(t);
        }

        public bool Delete(T t)
        {
            return db.Delete(t) > 0;
        }

        public bool DeleteById(object primaryKey)
        {
            return db.Delete<T>(primaryKey) > 0;
        }

        public bool Update(T t)
        {
            return db.Update(t) > 0;
        }

        public T Load(object primaryKey)
        {
            db.EnableAutoSelect = true;
            T t = db.SingleOrDefault<T>(primaryKey);
            db.EnableAutoSelect = false;
            return t;
        }

        public List<T> GetAll(string orderBy)
        {
            return GetList(null, orderBy);
        }

        public List<T> GetTopList(int topSize, Sql conditions, string orderBy)
        {
            return GetTopList(topSize, conditions, null, orderBy);
        }
        public List<T> GetTopList(int topSize, Sql conditions, List<JoinTable> joinTables, string orderBy)
        {
            Sql buider = new Sql();
            buider.Select("top " + topSize).Append(ParseField(joinTables)).From(tableName);
            if (joinTables != null)
            {
                foreach (JoinTable table in joinTables)
                {
                    if (table.JoinType == "left join")
                        buider.LeftJoin(table.TableName).On(table.ColumnName);
                    else
                        buider.InnerJoin(table.TableName).On(table.ColumnName);
                }
            }
            if (conditions != null) buider.Append(conditions);
            if (!string.IsNullOrEmpty(orderBy)) buider.OrderBy(orderBy);
            return db.Query<T>(buider).ToList();
        }
        public List<T> GetList(Sql conditions, string orderBy)
        {
            return GetList(conditions, null, orderBy);
        }

        public List<T> GetList(Sql conditions, List<JoinTable> joinTables, string orderBy)
        {
            Sql buider = new Sql();
            buider.Select(ParseField(joinTables)).From(tableName);
            if (joinTables != null)
            {
                foreach (JoinTable table in joinTables)
                {
                    if (table.JoinType == "left join")
                        buider.LeftJoin(table.TableName).On(table.ColumnName);
                    else
                        buider.InnerJoin(table.TableName).On(table.ColumnName);
                }
            }
            if (conditions != null)
                buider.Append(conditions);
            if (!string.IsNullOrEmpty(orderBy))
                buider.OrderBy(orderBy);
            return db.Query<T>(buider).ToList();
        }

        public List<T> GetJoinList(Sql conditions, List<JoinTable> joinTable, string orderBy)
        {
            Sql buider = new Sql();
            buider.Select(ParseField(joinTable)).From(tableName);
            if (joinTable != null)
            {
                foreach (JoinTable table in joinTable)
                {
                    if (table.JoinType == "left join")
                        buider.LeftJoin(table.TableName).On(table.ColumnName);
                    else
                        buider.InnerJoin(table.TableName).On(table.ColumnName);
                }
            }
            if (conditions != null)
                buider.Append(conditions);
            if (!string.IsNullOrEmpty(orderBy))
                buider.OrderBy(orderBy);
            return db.Query<T>(buider).ToList();
        }

        public Page<T> GetJoinPageList(int pageIndex, int pageSize, List<JoinTable> joinTable, Sql conditions, string orderBy)
        {
            Sql buider = new Sql();
            buider.Select(ParseField(joinTable)).From(tableName);
            if (joinTable != null)
            {
                foreach (JoinTable table in joinTable)
                {
                    if (table.JoinType == "left join")
                        buider.LeftJoin(table.TableName).On(table.ColumnName);
                    else
                        buider.InnerJoin(table.TableName).On(table.ColumnName);
                }
            }
            if (conditions != null)
                buider.Append(conditions);
            if (!string.IsNullOrEmpty(orderBy))
                buider.OrderBy(orderBy);
            return db.Page<T>(pageIndex, pageSize, buider);
        }

        public Page<T> GetPageList(int pageIndex, int pageSize, Sql conditions, string orderBy)
        {
            return GetJoinPageList(pageIndex, pageSize, null, conditions, orderBy);
        }

        public Page<T> GetPageList(int pageIndex, int pageSize, string orderBy)
        {
            return GetPageList(pageIndex, pageSize, null, orderBy);
        }

        public string GetSql()
        {
            return db.LastSQL;
        }

        public object ExecuteSql(string sql, params object[] args)
        {
            return db.Execute(sql, args);
        }

        private string ParseField(IEnumerable<JoinTable> joinTables)
        {
            StringBuilder builder = new StringBuilder();
            PropertyInfo[] propertyInfos = typeof(T).GetProperties();
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.GetCustomAttributes(typeof(ResultColumnAttribute), false).Length == 0)
                    builder.AppendFormat("[{0}].[{1}],", tableName, propertyInfo.Name);
            }
            if (joinTables != null)
            {
                foreach (JoinTable joinTable in joinTables)
                {
                    if (joinTable.ShowFields != null)
                        foreach (string s in joinTable.ShowFields)
                        {
                            builder.AppendFormat("[{0}].[{1}],", joinTable.TableName, s);
                        }
                }
            }
            return builder.ToString().Substring(0, builder.ToString().Length - 1);
        }

        public Database GetContext
        {
            get { return db; }
        }
    }

    public class DbContext
    {
        private static volatile Database db;
        private static readonly object sync = new object();


        private DbContext()
        {

        }

        public static Database GetDbContext()
        {
            if (db == null)
            {
                lock (sync)
                {
                    if (db == null)
                    {
                        db = new Database("ConnectionString") { EnableAutoSelect = false };
                    }
                }
            }
            return db;
        }
    }
}